package org.anachronos.jeep.core.trace.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlType;

@XmlType
@XmlAccessorType(XmlAccessType.FIELD)
public class Package extends NamedElement {
	@XmlElements(value = {
			@XmlElement(name = "class", type = Class.class),
			@XmlElement(name = "package", type = Package.class)
	})
	private Set<NamedElement> children = 
		new HashSet<NamedElement>();

	private transient Map<String, NamedElement> nameToElement =
		new HashMap<String, NamedElement>();
	
	/**
	 * Required by jaxb.
	 */
	public Package() {
		super();
	}

	public Package(final String name, final String id) {
		super(name, id);
	}
	
	public Set<NamedElement> getChildren() {
		return children;
	}

	public void add(final NamedElement element) {
		children.add(element);
		nameToElement.put(element.getName(), element);
	}

	public NamedElement getChild(final String name) {
		if (nameToElement == null) {
			initNameToElement();
		}
		return nameToElement.get(name);
	}

	@SuppressWarnings("unchecked")
	public <N extends NamedElement> N lookupOrCreate(final java.lang.Class<N> clazz, 
			final String... names) {
		try {
			Package pkg = this;
			N child = null;
			for (int i = 0; i < names.length; i++) {
				final String name = names[i];
				child = (N) pkg.getChild(name);
				if (child instanceof Package) {
					pkg = (Package) child;
				} else if (child == null) {
					child = clazz.newInstance();
					child.setName(name);
					child.setId(createId(i, names));
					pkg.add(child);
					if (child instanceof Package) {
						pkg = (Package) child;
					}
				}
			}
			return child;
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}
	}

	private String createId(int index, final String... names) {
		final StringBuffer buffer = new StringBuffer();
		for (int i = 0; i <= index; i++) {
			buffer.append(names[i]);
			if (i <= index - 1) {
				buffer.append("/");
			}
		}
		return buffer.toString();
	}
	
	private void initNameToElement() {
		nameToElement = new HashMap<String, NamedElement>();
		for (final NamedElement child : children) {
			nameToElement.put(child.getName(), child);
		}
	}
}
